use cgmath::{vec2, Vector2};

use crate::math::{PointExt, Rotation};

type Point = Vector2<i32>;

pub struct Model {
    pub val: u8,
    pub blocks: Vec<Point>,
}

impl Model {
    pub fn new<P: Into<Point>, I: IntoIterator<Item = P>>(val: u8, blocks: I) -> Self {
        Self {
            val,
            blocks: blocks.into_iter().map(Into::into).collect(),
        }
    }
}

pub struct Tetris<'model> {
    position: Point,
    rotation: Rotation,
    model: &'model Model,
}

impl<'m> Tetris<'m> {
    pub fn new(position: Point, model: &'m Model) -> Self {
        Self {
            position,
            rotation: Rotation::Idle,
            model,
        }
    }

    pub fn reset(&mut self, position: Point, model: &'m Model) {
        self.position = position;
        self.rotation = Rotation::Idle;
        self.model = model;
    }

    pub fn apply_movement(&mut self, movement: Point) {
        self.position += movement;
    }

    pub fn after_move_blocks(&self, movement: Point) -> Vec<Point> {
        self.model
            .blocks
            .iter()
            .map(|i| i.rotate(vec2(0, 0), self.rotation) + self.position + movement)
            .collect::<Vec<_>>()
    }

    pub fn apply_rotation(&mut self, rot: Rotation) {
        self.rotation += rot;
    }

    pub fn after_rotate_blocks(&self, rot: Rotation) -> Vec<Point> {
        self.model
            .blocks
            .iter()
            .map(|i| self.position + i.rotate(vec2(0, 0), self.rotation + rot))
            .collect::<Vec<_>>()
    }

    pub fn visit<F: FnMut(i32, i32, u8) -> bool>(&self, mut visitor: F) {
        for point in &self.model.blocks {
            let block_pos = self.position + point.rotate(vec2(0, 0), self.rotation);
            if !visitor(block_pos.x, block_pos.y, self.model.val) {
                break;
            }
        }
    }
    
    pub fn model(&self) -> &Model {
        self.model
    }

    pub fn rotation(&self) -> &Rotation {
        &self.rotation
    }

    pub fn position(&self) -> &Vector2<i32> {
        &self.position
    }
}
